ATOM Documentation

← Back to App

Stripe Production Setup Guide for Atom SaaS

This guide explains how to set up the Stripe products and prices required to support the Atom SaaS billing engine, including metered "Managed AI" usage.

1. Product & Price Configuration

You need to create the following products and prices in your Stripe Dashboard.

Core Subscription Plans

Create one **Recurring** product named "Atom SaaS" with the following tiers:

PlanPrice (Monthly)Stripe Price ModelAtoms Equivalent
**Solo**$19.00RecurringSTRIPE_PRICE_ID_SOLO
**Team**$79.00RecurringSTRIPE_PRICE_ID_TEAM
**Enterprise**$299.00RecurringSTRIPE_PRICE_ID_ENTERPRISE

Managed AI Usage (Metered)

We use two distinct Metered Prices to support the "Bundle" vs "Pay-As-You-Go" strategies.

1. For Model A (Bundled + Overage)

This price is attached to the **Solo** and **Team** subscriptions ($50/mo add-on base).

  • **Product Name**: "Managed AI Tokens (Bundled)"
  • **Pricing Model**: **Tiered** (Usage-based)
  • **Tiers**:
  1. **First 5,000,000 units**: $0.00 (Free Bundle)
  2. **Any usage after**: $0.000005 per unit ($5 per 1M)
  • **Env Variable**: STRIPE_PRICE_ID_MANAGED_AI_BUNDLED

2. For Model B (Pay-As-You-Go)

This price is attached to **Enterprise** or custom separate subscriptions.

  • **Product Name**: "Managed AI Tokens (Pure)"
  • **Pricing Model**: **Standard** (Usage-based)
  • **Price**: $0.00002 per unit (approx $20/1M - includes margin)
  • **Env Variable**: STRIPE_PRICE_ID_MANAGED_AI_PURE

---

2. Webhook Setup

Step 1: Add Endpoint

Go to Developers -> Webhooks and click "Add Endpoint".

  • **URL**: https://your-domain.com/api/billing/webhook
  • **Events to listen for**:
  • checkout.session.completed
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.payment_succeeded

Step 2: Configure Secret

Copy the **Signing Secret** (whsec_...) and add it to your Vault secrets:

atom-cli secrets set STRIPE_WEBHOOK_SECRET=whsec_...

---

3. Developer Integration

Linking Tenant to Stripe Usage

Atom uses the stripe_usage_item_id to report consumption. When a subscription is created, you must store the id of the metered price item on the Tenant model to enable real-time reporting.

# During checkout.session.completed
subscription = stripe.Subscription.retrieve(session.subscription)

# Determine strictly metered usage item ID based on plan/logic
# In a real app, this logic maps Plan -> Price ID
price_id_bundled = os.getenv('STRIPE_PRICE_ID_MANAGED_AI_BUNDLED')
price_id_pure = os.getenv('STRIPE_PRICE_ID_MANAGED_AI_PURE')

# Find the usage item in the subscription
usage_item = next((item for item in subscription['items']['data'] 
                  if item.price.id in [price_id_bundled, price_id_pure]), None)

if usage_item:
    # Store usage_item.id (e.g. "si_...") on the Tenant model
    tenant.stripe_usage_item_id = usage_item.id
    tenant.stripe_subscription_id = subscription.id

---

4. Reporting Usage (Cron Job)

To actually bill customers, you must push aggregated usage to Stripe periodically (e.g., hourly).

`backend-saas/scripts/report_usage.py`

Create a script that:

  1. Aggregates unbilled TokenUsage records for each tenant.
  2. Pushes the sum to Stripe's Usage Record API.
  3. Marks tokens as billed=True.
import stripe
import os
from sqlalchemy import func
from core.models import TokenUsage, Tenant
from core.database import SessionLocal

def report_usage():
    db = SessionLocal()
    stripe.api_key = os.getenv("STRIPE_SECRET_KEY")

    # 1. Aggregate unbilled usage by tenant
    usage_aggregates = (
        db.query(
            TokenUsage.tenant_id, 
            func.sum(TokenUsage.input_tokens + TokenUsage.output_tokens).label("total_tokens")
        )
        .filter(TokenUsage.billed == False)
        .group_by(TokenUsage.tenant_id)
        .all()
    )

    for tenant_id, total_tokens in usage_aggregates:
        tenant = db.query(Tenant).filter(Tenant.id == tenant_id).first()
        if not tenant or not tenant.stripe_usage_item_id:
            continue
            
        try:
            # 2. Push to Stripe
            stripe.SubscriptionItem.create_usage_record(
                tenant.stripe_usage_item_id,
                quantity=int(total_tokens),
                timestamp=int(time.time()),
                action='increment',
            )
            
            # 3. Mark as billed
            db.query(TokenUsage).filter(
                TokenUsage.tenant_id == tenant_id, 
                TokenUsage.billed == False
            ).update({"billed": True})
            
            db.commit()
            print(f"Reported {total_tokens} for tenant {tenant_id}")
            
        except Exception as e:
            print(f"Failed to report usage for {tenant_id}: {e}")
            db.rollback()

if __name__ == "__main__":
    report_usage()